package com.lam.common.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.lam.common.config.AppBaseConfig;
import com.lam.common.context.ContextHolder;
import com.lam.common.exception.BusinessException;
import com.lam.common.utils.file.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

/**
 * 客户端工具类
 * 
 * @author lam
 */
public class ServletUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(ServletUtils.class);
	
	public static final String CHARSET = "utf-8";
	public static final String DEFAULT_CHARSET = "ISO8859-1";
	public static final String FILE_SUFFIX_SEPARATOR = ".";
	public static final String CONCATOR = "_";
	
	/**
	 * 获取String参数
	 */
	public static String getParameter(String name) {
		return getRequest().getParameter(name);
	}

	/**
	 * 获取String参数
	 */
	public static String getParameter(String name, String defaultValue) {
		return ConvertUtil.toStr(getRequest().getParameter(name), defaultValue);
	}

	/**
	 * 获取request
	 */
	public static HttpServletRequest getRequest() {
		return getRequestAttributes().getRequest();
	}

	/**
	 * 获取response
	 */
	public static HttpServletResponse getResponse() {
		return getRequestAttributes().getResponse();
	}

	/**
	 * 获取session
	 */
	public static HttpSession getSession() {
		return getRequest().getSession();
	}

	public static ServletRequestAttributes getRequestAttributes() {
		RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
		return (ServletRequestAttributes) attributes;
	}
	
	/**
	 * 是否json格式的请求
	 * @param request
	 * @return
	 */
	public static boolean isJsonRequest(HttpServletRequest request) {
		return StringUtils.containsIgnoreCase(request.getContentType(), "application/json");
	}

	/**
	 * 是否是Ajax异步请求
	 * 
	 * @param request
	 */
	public static boolean isAjaxRequest(HttpServletRequest request) {
		String accept = request.getHeader("accept");
		if (StringUtils.containsIgnoreCase(accept, "application/json")) {
			return true;
		}
		
		String contentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
		if (StringUtils.containsIgnoreCase(contentType, "application/json")) {
			return true;
		}

		String xRequestedWith = request.getHeader("X-Requested-With");
		if (StringUtils.contains(xRequestedWith, "XMLHttpRequest")) {
			return true;
		}

		String uri = request.getRequestURI();
		if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
			return true;
		}

		String ajax = request.getParameter("__ajax");
		return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
	}
	
	/**
	 * 获取当前的web应用上下文
	 * 
	 * @return
	 * @date 2017年5月2日
	 * @author lam
	 */
	public static WebApplicationContext getCurrentWebApplicationContext() {
		HttpServletRequest request = getRequest();
		if(request == null){
			return null;
		}
		return WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
	}

	/**
	 * 根据beanname获取Bean组件
	 * 
	 * @param beanName
	 * @return
	 * @date 2017年5月2日
	 * @author lam
	 */
	public static Object getBean(String beanName) {
		ApplicationContext wac = ContextHolder.getApplicationContext();
		if (wac == null) {
			logger.info("当前应用上下文为空");
			return null;
		}
		return wac.getBean(beanName);
	}

	/**
	 * 根据beanType获取Bean组件
	 * 
	 * @param clazz
	 * @return
	 * @date 2017年5月2日
	 * @author lam
	 */
	public static <T> T getBean(Class<T> clazz) {
		ApplicationContext wac = ContextHolder.getApplicationContext();
		if (wac == null) {
			logger.info("当前应用上下文为空");
			return null;
		}
		return wac.getBean(clazz);
	}
	
	/**
	 * 返回值
	 * 
	 * @param str
	 * @date 2016年12月2日
	 * @author lam
	 */
	public static void outWrite(String str) {
		outWrite(null, str);
	}
	
	public static void outWrite(HttpServletResponse response, String str) {
		outWrite(response, null, str);
	}
	
	/**
	 * 返回值
	 * 
	 * @param str
	 * @date 2016年12月2日
	 * @author lam
	 */
	public static void outWrite(HttpServletResponse response, Integer httpStatus, String str) {
		try {
			if(response == null){
				response = getResponse();
			}
			if (response != null) {
				if(httpStatus != null ) {
					response.setStatus(httpStatus);
				}
				response.setContentType(MediaType.APPLICATION_JSON_VALUE);
				response.setCharacterEncoding(CHARSET);
				response.getWriter().write(str);
			} else {
				logger.error("response is null");
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
	}

	public static void outWrite(HttpServletResponse response, Object obj) {
		outWrite(response, JsonUtil.toJsonString(obj));
	}
	
	public static void outWrite(HttpServletResponse response, Integer httpStatus, Object obj) {
		outWrite(response, httpStatus, JsonUtil.toJsonString(obj));
	}
	
	public static void outWrite(Object obj) {
		outWrite(JsonUtil.toJsonString(obj));
	}


	/**
	 * 输出文件（前端下载文件）
	 * 
	 * @param filePath 路径,完整的文件路径
	 * @date 2016年11月9日
	 * @author lam
	 */
	public static void outWriteFile(String filePath) {
		outWriteFile(filePath, null);
	}
	
	/**
	 * 输出文件（前端下载文件）
	 * 
	 * @param filePath 路径,完整的文件路径
	 * @date 2016年11月9日
	 * @author lam
	 */
	public static void outWriteFile(String filePath, String contentType) {
		outWriteFile(filePath, null, contentType);
	}

	/**
	 * 输出文件（前端下载文件）
	 * 
	 * @param filePath 路径
	 * @param displayFileName 输出到前端的文件名称
	 * @date 2016年11月9日
	 * @author lam
	 */
	public static void outWriteFile(String filePath, String displayFileName, String contentType) {
		
//		filePath = filePath.startsWith(File.separator) ? filePath : AppBaseConfig.me().getUploadPath() + File.separator + filePath;
		
		File file = new File(filePath);
		if (!file.exists()) {
			logger.error("path:{}{}{}", filePath);
			throw new BusinessException("未找到文件");
		}
		if (StringUtils.isBlank(displayFileName)) {
			displayFileName = StringUtils.substringAfterLast(filePath, "/");
		}
		try (InputStream in = new FileInputStream(file);){
			outWriteFile(in, displayFileName, contentType);
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException("未找到文件");
		} 
	}

	/**
	 * 输出文件（前端下载文件）
	 * 
	 * @param is 要下载的文件流
	 * @param displayFileName 输出到前端的文件名称
	 * @date 2016年11月9日
	 * @author lam
	 */
	public static void outWriteFile(InputStream is, String displayFileName, String contentType) {
		
		HttpServletResponse response = getResponse();
		try (BufferedInputStream in = new BufferedInputStream(is);
				BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());){
			// 解决中文乱码和不显示问题
			displayFileName = new String(displayFileName.getBytes(CHARSET), DEFAULT_CHARSET);
			response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(displayFileName, DEFAULT_CHARSET));
			if(StringUtils.isNotBlank(contentType)) {
				response.setContentType(contentType);
				if(contentType.startsWith("image")) {//对图片设置浏览器缓存，缓存1天
					response.setHeader("Cache-Control", "max-age=86400");
				}
			}else {
				response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
			}
			
			setContentRange(response, in);

			byte[] data = new byte[1024*10];
			int len = 0;
			while (-1 != (len = in.read(data, 0, data.length))) {
				out.write(data, 0, len);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException("下载文件异常");
		} finally {
			close(is);
		}
	}

	private static void setContentRange(HttpServletResponse response, BufferedInputStream in) throws IOException {
		int total = in.available();
		response.setHeader("Content-Length", String.valueOf(total));
		Long pos = getPartialPos();
		if(pos != null){
			//支持断点续传
			response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
			response.setHeader("Accept-Ranges", "bytes");
			long size = in.skip(pos);
			//Content-Range: bytes 0-800/801 //801:文件总大小
			response.setHeader("Content-Range", String.format("bytes %s-%s/%s", size, (total-1), total));
		}
	}

	private static Long getPartialPos(){
		HttpServletRequest request = getRequest();
		if(request == null){
			return 0L;
		}
		String rangeStr = request.getHeader("Range");
		rangeStr = StringUtils.isBlank(rangeStr) ? request.getHeader("range") : rangeStr;
		if (rangeStr == null) {
			return null;
		}
		Long pos = null;
		try {
			pos = Long.parseLong(rangeStr.replaceAll("bytes=", "").replaceAll("-", ""));
		} catch (NumberFormatException e) {
			logger.error("{} is not Number!", rangeStr);
		}
		return pos;
	}

	public static void close(Closeable c){
		try {
			if (c != null) {
				c.close();
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 获取表单提交的参数
	 * 
	 * @param clazz
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static <T> T getFormParamter(Class<T> clazz) {
		return JsonUtil.toJavaObject(getFormParamter(), clazz);
	}

	/**
	 * 获取表单提交的参数
	 * 
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static Map<String, Object> getFormParamter() {
		HttpServletRequest request = null;
		return getFormParamter(request);
	}

	/**
	 * 获取表单提交的参数，并转换成对象
	 * 
	 * @param request
	 * @param clazz
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static <T> T getFormParamter(HttpServletRequest request, Class<T> clazz) {
		return JsonUtil.toJavaObject(getFormParamter(request), clazz);
	}

	/**
	 * 获取表单提交的参数
	 * 
	 * @param requestPara
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static Map<String, Object> getFormParamter(HttpServletRequest requestPara) {
		HttpServletRequest request = null;
		if (requestPara == null) {
			request = getRequest();
		} else {
			request = requestPara;
		}

		Map<String, Object> json = new HashMap<>();
		if (request == null) {
			return json;
		}
		Enumeration<String> paramNames = request.getParameterNames();
		String[] values = null;
		while (paramNames.hasMoreElements()) {
			String name = paramNames.nextElement();
			values = request.getParameterValues(name);
			if (values == null || values.length == 0) {
				continue;
			}
			if (values.length == 1) {
				json.put(name, values[0]);
			} else {
				json.put(name, values);
			}
		}
		return json;
	}

	public static Map<String, Object> getParamter() {
		HttpServletRequest request = null;
		return getParamter(request);
	}

	/**
	 * 获取参数
	 * 
	 * @return
	 * @date 2016年10月19日
	 * @author lam
	 */
	public static Map<String, Object> getParamter(HttpServletRequest requestPara) {
		Map<String, Object> params = new HashMap<>();
		HttpServletRequest request = null;
		if (requestPara == null) {
			request = getRequest();
		} else {
			request = requestPara;
		}
		if (request != null) {
			transferParam(params, request);
		}
		return params;
	}

	private static void transferParam(Map<String, Object> params, HttpServletRequest request) {
		if(params!= null){
			Map<?, ?> requestParams = request.getParameterMap();
			for (Iterator<?> iter = requestParams.keySet().iterator(); iter.hasNext();) {
				String name = (String) iter.next();
				String[] values = (String[]) requestParams.get(name);
				String valueStr = "";
				for (int i = 0; i < values.length; i++) {
					valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
				}
				params.put(name, valueStr);
			}
		}
	}

	/**
	 * 将地址参数转成对象
	 * 
	 * @param clazz
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static <T> T getParamter(HttpServletRequest request, Class<T> clazz) {
		return JsonUtil.toJavaObject(getParamter(request), clazz);
	}

	/**
	 * 将地址参数转成对象
	 * 
	 * @param clazz
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static <T> T getParamter(Class<T> clazz) {
		return JsonUtil.toJavaObject(getParamter(), clazz);
	}

	/**
	 * 将body中的数据转换成json对象
	 * 
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static Map<String, Object> getParamterBody() {
		return JsonUtil.parseJSON(getParamterBodyString());
	}

	/**
	 * 将body中的数据转成对象
	 * 
	 * @param clazz
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static <T> T getParamterBody(Class<T> clazz) {
		return JsonUtil.toJavaObject(getParamterBodyString(), clazz);
	}

	/**
	 * 获取body中的数据
	 * 
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static String getParamterBodyString(HttpServletRequest request) {
		if (request == null) {
			request = getRequest();
		} 
		try {
			if (request != null) {
				return StreamUtils.readStreamString(request.getInputStream(), CHARSET);
			} else {
				return null;
			}
		} catch (IOException e) {
			logger.error("获取字符流数据失败", e);
			return null;
		}
	}

	/**
	 * 获取body中的数据
	 * 
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static String getParamterBodyString() {
		return getParamterBodyString(null);
	}

	/**
	 * 获取body中的字节数据
	 * 
	 * @return
	 * @date 2017年3月18日
	 * @author lam
	 */
	public static byte[] getParamterBodyByte() {
		try {
			HttpServletRequest request = getRequest();
			if (request == null) {
				return new byte[0];
			}
			return StreamUtils.readStreamByte(request.getInputStream());
		} catch (IOException e) {
			logger.error("获取字节流数据失败", e);
			return new byte[0];
		}
	}
	
	/**
     * 从request请求中获取MultipartFile
     *
     * @param request
     */
    public static List<MultipartFile> getMultipartFile(HttpServletRequest request) {
        List<MultipartFile> multiFileList = new ArrayList<>();
        if (request == null) {
            return multiFileList;
        }
        // 创建一个通用的多部分解析器
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getServletContext());
        if (!multipartResolver.isMultipart(request)) {
            logger.error("多文件上传请求异常.");
            return multiFileList;
        }

        try {
            // 转换成多部分request
            MultipartHttpServletRequest multiRequest = multipartResolver.resolveMultipart(request);
            Map<String, MultipartFile> multiFileMap = multiRequest.getFileMap();
            for (Entry<String, MultipartFile> entry : multiFileMap.entrySet()) {
                multiFileList.add(entry.getValue());
            }
        } catch (Exception e) {
            logger.error("上传文件异常", e);
            return multiFileList;
        }
        if (multiFileList.isEmpty()) {
            logger.error("未解析到任何文件");
        }
        return multiFileList;
    }
	
	public static List<String> uploadStreamToFile(String dirPath, MultipartFile... files) {
		return convertFile(dirPath, Arrays.asList(files));
	}

	/**
	 * 将request请求中的文件流写进临时目录中，生成临时文件
	 * 
	 * @return
	 * @date 2016年12月2日
	 * @author lam
	 */
	public static List<String> uploadStreamToFile() {
		String dirPath = null;
		Map<String, Object> params = null;
		return uploadStreamToFile(dirPath, params);
	}

	public static MultipartHttpServletRequest getMultipartHttpServletRequest() {
		HttpServletRequest request = getRequest();
		if(request == null ) {
			return null;
		}
		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
		return multipartResolver.resolveMultipart(request);
	}
	
	/**
	 * 将request请求中的文件流写进临时目录中，生成临时文件
	 * 
	 * @param dirPath
	 * @return
	 * @date 2016年12月2日
	 * @author lam
	 */
	public static List<String> uploadStreamToFile(String dirPath, Map<String, Object> params) {
		HttpServletRequest request = getRequest();
		if(request == null) {
			return Collections.emptyList();
		}
		
		MultipartHttpServletRequest multiRequest;
		if(request instanceof MultipartHttpServletRequest){
			multiRequest = ((MultipartHttpServletRequest) request);
		}else{
			// 创建一个通用的多部分解析器
			CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getSession().getServletContext());
			if (!multipartResolver.isMultipart(request)) {
				logger.error("多文件上传请求异常.");
				throw new BusinessException("文件上传请求异常");
			}

//			if (StringUtils.isBlank(dirPath)) {
//				logger.error("临时目录不能为空");
//				throw new BusinessException("临时目录不能为空");
//			}
			// 转换成多部分request
			multiRequest = multipartResolver.resolveMultipart(request);
		}
		
		transferParam(params, multiRequest);
		return convertFile(dirPath, multiRequest.getFileMap().values());
	}

	private static List<String> convertFile(String dirPath, Collection<MultipartFile> files) {
		
		if(StringUtils.isBlank(dirPath)){
			dirPath = AppBaseConfig.me().getUploadTmpPath();
		}
		if(!FileUtil.mkdir(dirPath)) {
			logger.error("创建文件目录异常, dirPath:{}", dirPath);
			throw new BusinessException("获取文件列表发生未知异常");
		}
		try {
			return copy(dirPath, files);
		} catch (BusinessException e) {
			logger.error(e.getMessage());
			throw e;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new BusinessException("上传文件异常");
		}
	}

	private static List<String> copy(String dirPath, Collection<MultipartFile> files) throws IOException {
		
		List<String> filePathList = new ArrayList<>();
		int i = 1;
		for (MultipartFile file : files) {
			if (file == null || StringUtils.isBlank(file.getOriginalFilename())) {
				logger.error("第 {}个上传的文件为空", i);
				continue;
			}
			String filename = file.getOriginalFilename();
			String newFileName = fileNameDateFormat(filename);
			String fileSavePath = dirPath + File.separatorChar + newFileName;
			logger.info("第{}个上传文件：{},重命名后的文件：{},文件位置：{}", i, filename, newFileName, fileSavePath);
			File fileTmp = new File(fileSavePath);
			if(!fileTmp.exists() && !fileTmp.createNewFile()){
				logger.error("创建临时文件失败, fileTmp:{}", fileTmp);
				throw new BusinessException("上传文件异常, 提示:创建临时文件失败!");
			}
			file.transferTo(fileTmp);
			filePathList.add(fileSavePath);
			i++;
		}
		return filePathList;
	}

	public static String fileNameDateFormat(String filename) {
		// 文件命名格式 XXX_YYYYMMDDHHmmss.后缀
		if (filename.indexOf(FILE_SUFFIX_SEPARATOR) != -1) {
			int lastIndex = filename.lastIndexOf(FILE_SUFFIX_SEPARATOR);
			return filename.substring(0, lastIndex) + CONCATOR + DateUtil.formatDate(new Date(), DateUtil.PATTERN_YYYYMMDDHHMMSS)
					+ filename.substring(lastIndex);
		} else {
			return filename + CONCATOR + DateUtil.formatDate(new Date(), DateUtil.PATTERN_YYYYMMDDHHMMSS);
		}
	}
}
